-- UIGuideDialogue
-- Created by zouyb
-- 通用引导对话窗口

require "game/ui/module/guide/GuideSlimeDisplay"

UIGuideDialogue = class("UIGuideDialogue", function()
    return cc.Layer:create();
end);

function UIGuideDialogue.create(showType, msgList, pos, inEffect, outEffect)
    return UIGuideDialogue.new(showType, msgList, pos, inEffect, outEffect);
end

-- 构造函数
function UIGuideDialogue:ctor(showType, msgList, pos, inEffect, outEffect)
    self:setName("UIGuideDialogue");
    local node = cc.CSLoader:createNode("layout/guide/GuideDialogue.csb");
    self:addChild(node);
    self.node = node;
    self.showType = showType;
    self.msgList = msgList;
    self.pos = pos;

    self.inEffectName = inEffect;
    self.outEffectName = outEffect;

    self:setLocalZOrder(UIMgr.BONUS_NOTIFY_ZORDER + 1);

    -- 初始化
    self:initParams();
    self:init();

    local function callback()
        -- 开始对话
        self:talk();
    end

    -- 运行入场效果
    self:runInEffect(callback);

    -- 适配
    self:resize();
end

-- 根据showType初始化参数
function UIGuideDialogue:initParams()
    if self.showType == 1 then
        self.bgIndex = 1;
        self.displayType1 = 3;
        self.displayType2 = 1;
    elseif self.showType == 2 then
        self.bgIndex = 2;
        self.displayType1 = 3;
        self.displayType2 = 1;
    elseif self.showType == 3 then
        self.bgIndex = 1;
        self.displayType1 = 3;
        self.displayType2 = 2;
    elseif self.showType == 4 then
        self.bgIndex = 2;
        self.displayType1 = 3;
        self.displayType2 = 2;
    elseif self.showType == 5 then
        self.bgIndex = 1;
        self.displayType1 = 0;
        self.displayType2 = 1;
    elseif self.showType == 6 then
        self.bgIndex = 1;
        self.displayType1 = 3;
        self.displayType2 = 4;
    end
end

-- 初始化
function UIGuideDialogue:init()
    -- 勇者冈布奥头像
    local headIcon1 = findChildByName(self.node, "CT/dialogue/head_icon1");
    if self.displayType1 > 0 then
        self.display1 = GuideSlimeDisplay.create(self.displayType1);
        headIcon1:addChild(self.display1);
        headIcon1:setVisible(false);
        headIcon1.oldZOrder = headIcon1:getLocalZOrder();
    end

    -- 冒险者冈布奥头像
    local headIcon2 = findChildByName(self.node, "CT/dialogue/head_icon2");
    if self.displayType2 > 0 then
        self.display2 = GuideSlimeDisplay.create(self.displayType2);
        headIcon2:addChild(self.display2);
        headIcon2:setVisible(false);
        headIcon2.oldZOrder = headIcon1:getLocalZOrder();
    end

    -- 对话框位置
    local dialogue = findChildByName(self.node, "CT/dialogue");
    dialogue:setPosition(self.pos);

    -- 背景透明度
    local formBg = findChildByName(self.node, "bg/bgImg");
    formBg:setOpacity(1);

    local bg = findChildByName(self.node, "CT/dialogue/bg" .. self.bgIndex);

    -- 冈布奥名称
    local nameLabel = findChildByName(bg, "name");
    if self.showType == 2 or self.showType == 4 then
        TextStyleM.setTextStyle(nameLabel, TextStyleM.TEXT_SIZE_SMALL, TextStyleM.TEXT_COLOR_GREEN, false);
        nameLabel:setString(getLocStr("brave_slime"));
    else
        TextStyleM.setTextStyle(nameLabel, TextStyleM.TEXT_SIZE_SMALL, TextStyleM.TEXT_COLOR_DARK_RED, false);
        nameLabel:setString(getLocStr("adventurer_slime"));
    end

    -- 记录老的文本框位置
    self.textLabel = findChildByName(bg, "panel/text");
    self.oldTextLabelPos = cc.p(self.textLabel:getPosition());

    -- 注册点击处理
    local formBg = findChildByName(self.node, "bg/bgImg");
    formBg:addTouchEventListener(function() end);
end

-- 英雄躲到幕后
function UIGuideDialogue:darkHero(heroNode)
    heroNode:setLocalZOrder(-1);
end

-- 英雄来到台前
function UIGuideDialogue:highlightHero(heroNode)
    heroNode:setLocalZOrder(heroNode.oldZOrder);
end

-- 同时显示
function UIGuideDialogue:showBoth()
    local headIcon1 = findChildByName(self.node, "CT/dialogue/head_icon1");
    headIcon1:setVisible(true);
    local headIcon2 = findChildByName(self.node, "CT/dialogue/head_icon2");
    headIcon2:setVisible(true);
end

-- 初始化对话内容
function UIGuideDialogue:initDialogue()
    -- 还原位置
    self.textLabel:setPosition(self.oldTextLabelPos);

    -- 初始隐藏箭头
    local bg = findChildByName(self.node, "CT/dialogue/bg" .. self.bgIndex);
    local arrow = findChildByName(bg, "arrow");
    arrow:setVisible(false);

    -- 说话内容
    -- 泰语版本还要缩小
    local textScale = 1;
    if getLang() == "th" then
        textScale = 0.85;
    end
    TextStyleM.setTextStyle(self.textLabel, TextStyleM.TEXT_SIZE_SMALL, TextStyleM.TEXT_COLOR_DARK_BROWN, false, nil, nil, nil, nil, TextStyleM.TEXT_SIZE_SMALL, nil, TextStyleM.getTextScale(self.msgList[1], 19 * 2 * 3) * textScale);
    self.textLabel = applyString(self.textLabel, self.msgList[1]);

    -- 收集字符列表
    self:collectLetters(self.msgList[1]);

    -- 删除初始化过的
    table.remove(self.msgList, 1);
end

-- 开始对话
function UIGuideDialogue:talk()
    -- 初始化对话内容
    self:initDialogue();

    local showType = self.showType;

    -- 点击处理函数
    local function onClick(sender, eventType)
        if eventType == ccui.TouchEventType.began then
        elseif eventType == ccui.TouchEventType.ended then
            if not self.isFinished then
                -- 略过该段对话
                self:skipSection();
            else
                -- 开始下一段对话
                self:showNextSection();
            end
        end
    end

    -- 注册点击处理
    local formBg = findChildByName(self.node, "bg/bgImg");
    formBg:addTouchEventListener(onClick);

    -- 根据showType显示对话类型
    local headIcon1 = findChildByName(self.node, "CT/dialogue/head_icon1");
    local headIcon2 = findChildByName(self.node, "CT/dialogue/head_icon2");

    local bg = findChildByName(self.node, "CT/dialogue/bg" .. self.bgIndex);
    if showType == 2 or showType == 4 then
        -- 勇者冈布奥说话
        self:darkHero(headIcon2);
        self:highlightHero(headIcon1);
        self.mainTalker = self.display1;
    else
        -- 冒险者冈布奥说话
        self:darkHero(headIcon1);
        self:highlightHero(headIcon2);
        self.mainTalker = self.display2;
    end

    -- 泡泡先冒出来
    bg:setVisible(true);
    bg:setScale(0.3);
    local scaleTo = cc.ScaleTo:create(0.1, 1);
    local callFunc = cc.CallFunc:create(function()
        -- 播放文字效果
        self:playTextEffect();
    end);
    bg:runAction(cc.Sequence:create(scaleTo, callFunc));
end

-- 收集字符列表
function UIGuideDialogue:collectLetters(text)
    local textLabel = self.textLabel;
    local count = string.getWCharCount(text);
    self.letters = {};

    local i = 0;
    local endPos = count - 1;
    local charPos = 0;
    local curPaddingCount = 0;
    local lineNum = 0;
    while i <= endPos do
        local letter = textLabel:getVirtualRenderer():getLetter(i);
        local nextLetter = textLabel:getVirtualRenderer():getLetter(i+1);
        if letter then
            -- 初始所有字符置为不可见
            letter:setOpacity(0);
            letter.lineNum = lineNum;

            -- 加入到字符列表
            table.insert(self.letters, 1, i);
        else
            if nextLetter ~= nil then
                lineNum = lineNum + 1;

                if not string.isBreakLineChar(text, charPos-curPaddingCount) then
                    -- 如果是系统自动添加的换行符，需要把结束位置往后挪
                    endPos = endPos + 1;
                    curPaddingCount = curPaddingCount + 1;
                end
            end
        end

        i = i + 1;
        charPos = charPos + 1;
    end
end

-- 对话效果，一个字一个字慢慢播放
function UIGuideDialogue:playTextEffect()
    -- 开始说话
    self.mainTalker:gotoTalk();

    local duration = 0.1;
    if getLang() ~= "zh" and getLang() ~= "tw" then
        duration = 0.05;
    end
    local function play()
        if #self.letters > 0 then
            local pos = self.letters[#self.letters];
            local letter = self.textLabel:getVirtualRenderer():getLetter(pos);
            table.remove(self.letters, #self.letters);
            local fadeIn = cc.FadeIn:create(duration);
            local callFunc = cc.CallFunc:create(function()
                if 0 == #self.letters then
                    print("finished");

                    -- 箭头开始上下动
                    self:playArrowEffect();

                    -- 停止说话
                    self.mainTalker:gotoIdle();

                    -- 标记字体播放完成
                    self.isFinished = true;
                end
            end);

            self.isFinished = false;
            if letter then
                letter:runAction(cc.Sequence:create(fadeIn, callFunc));
            end
            performWithDelay(self.textLabel, play, duration);
        end
    end

    play();
end

-- 播放下一段对话
function UIGuideDialogue:showNextSection()
    local moveTo = cc.MoveBy:create(0.2, cc.p(0, 85));
    local callFunc = cc.CallFunc:create(function()
        if #self.msgList == 0 then
            local function callback()
                -- 关闭界面
                UIMgr.getCurrentScene():removeFormByName("UIGuideDialogue");
            end
            -- 播放离场动画后，关闭界面
            self:runOutEffect(callback);
        else
            -- 开始下一段讲话
            self:initDialogue();

            -- 播放文字效果
            self:playTextEffect();
        end
    end);
    self.textLabel:runAction(cc.Sequence:create(moveTo, callFunc));
end

-- 文字立刻显示
function UIGuideDialogue:skipSection()
    self.textLabel:stopAllActions();

    while #self.letters > 0 do
        local pos = self.letters[#self.letters];
        local letter = self.textLabel:getVirtualRenderer():getLetter(pos);
        table.remove(self.letters, #self.letters);

        if letter then
            letter:setOpacity(255);
        end

        if 0 == #self.letters then
            print("finished");

            -- 箭头开始上下动
            self:playArrowEffect()

            -- 标记字体播放完成
            self.isFinished = true;
        end
    end
end

-- 箭头上下移动
function UIGuideDialogue:playArrowEffect()
    local bg = findChildByName(self.node, "CT/dialogue/bg" .. self.bgIndex);
    local arrow = findChildByName(bg, "arrow");
    arrow:setVisible(true);
    if not arrow.isPlaying then
        local oldPos = cc.p(arrow:getPosition());

        local moveDown = cc.MoveTo:create(0.5, cc.pAdd(oldPos, cc.p(0, -5)));
        local moveBack = cc.MoveTo:create(0.5, oldPos);
        arrow:runAction(cc.RepeatForever:create(cc.Sequence:create(moveDown, moveBack)));
        arrow.isPlaying = true;
    end
end

-- 适配
function UIGuideDialogue:resize()
    local node = self.node;

    AlignM.fitToScreen(node, "bg");

    -- CT中的蒙版适配
    local mask = findChildByName(self.node, "CT/dialogue/mask");
    local w = AlignM.frameSize.width;
    local h = AlignM.frameSize.height;
    local scale = math.min(w / DESIGN_WIDTH, h / DESIGN_HEIGHT);
    mask:setContentSize(w / scale, h / scale);
    mask:setPosition(cc.p(-self.pos.x, -self.pos.y));
    mask:setOpacity(150);

    AlignM.alignToCenter(node, "CT");
end

-- 运行入场效果
function UIGuideDialogue:runInEffect(callback)
    if self.inEffectName == nil or self.inEffectName == "" then
        -- 没有入场效果，直接显示
        self:showBoth();

        callback();
        return;
    end

    if self.inEffectName == "A_JUMP_IN" then
        -- 冒险者冈布奥跳进来
        self:advSlimeJumpIn(callback);
    elseif self.inEffectName == "B_FLY_IN" then
        -- 勇者冈布奥飞入
        self:braveSlimeFlyIn(callback);
    elseif self.inEffectName == "AB_FLY_IN" then
        -- 说话者先飞入，另一个随后飞入
        self:bothFlyIn(callback);
    else
        print("入场效果未支持："..self.inEffectName);
    end
end

-- 冒险者冈布奥跳进来
function UIGuideDialogue:advSlimeJumpIn(callback)
    -- 冒险者冈布奥从右边跳进来
    local headIcon2 = findChildByName(self.node, "CT/dialogue/head_icon2");
    headIcon2:setVisible(true);

    local oldPos = cc.p(headIcon2:getPosition());
    headIcon2:setPositionX(DESIGN_WIDTH);
    local jump = cc.JumpTo:create(0.5, oldPos, 20, 3);
    local callFunc = cc.CallFunc:create(function()
        callback();
    end);

    headIcon2:runAction(cc.Sequence:create(jump, callFunc));
end

-- 勇者冈布奥进场动画
function UIGuideDialogue:braveSlimeFlyIn(callback)
    local headIcon2 = findChildByName(self.node, "CT/dialogue/head_icon2");
    headIcon2:setVisible(true);
    self:darkHero(headIcon2);

    -- 勇者冈布奥渐现
    local headIcon1 = findChildByName(self.node, "CT/dialogue/head_icon1");
    headIcon1:setVisible(true);
    headIcon1:setOpacity(0);

    local fadeIn = cc.FadeIn:create(0.5);
    local callFunc = cc.CallFunc:create(function()
        callback();
    end);

    headIcon1:runAction(cc.Sequence:create(fadeIn, callFunc));
end

-- 说话者先飞入，另一个随后飞入
function UIGuideDialogue:bothFlyIn(callback)
    -- 冒险者飞入
    local advFlyIn = cc.CallFunc:create(function()
        local headIcon2 = findChildByName(self.node, "CT/dialogue/head_icon2");
        headIcon2:setVisible(true);
        local oldPos = cc.p(headIcon2:getPosition());
        headIcon2:setPositionX(oldPos.x + DESIGN_WIDTH);
        headIcon2:runAction(cc.MoveTo:create(0.2, oldPos));
    end);

    -- 勇者飞入
    local braveFlyIn = cc.CallFunc:create(function()
        local headIcon1 = findChildByName(self.node, "CT/dialogue/head_icon1");
        headIcon1:setVisible(true);
        local oldPos = cc.p(headIcon1:getPosition());
        headIcon1:setPositionX(oldPos.x-DESIGN_WIDTH);
        headIcon1:runAction(cc.MoveTo:create(0.2, oldPos));
    end);

    local delay = cc.DelayTime:create(0.35);
    local callFunc = cc.CallFunc:create(function()
        callback();
    end);

    local firstSlime, secondSlime;
    if self.showType == 2 or self.showType == 4 then
        -- 勇者先飞入，x秒后冒险者飞入
        self:runAction(cc.Sequence:create(braveFlyIn, delay, advFlyIn, callFunc));
    else
        -- 冒险者先飞入，x秒后勇者飞入
        self:runAction(cc.Sequence:create(advFlyIn, delay, braveFlyIn, callFunc));
    end
end

-- 运行离开效果
function UIGuideDialogue:runOutEffect(callback)
    if self.outEffectName == nil or self.outEffectName == "" then
        callback();
        return;
    end

    if self.isRunningOutEffect then
        return;
    end

    if self.outEffectName == "AB_FLY_OUT" then
        -- 冒险者冈布奥和勇者冈布奥同时飞出
        self:bothFlyOut(callback);
    elseif self.outEffectName == "B_LEFT" then
        -- 勇者冈布奥离开
        self:braveLeft(callback);
    else
        print("出场效果未支持："..self.outEffectName);
    end

    self.isRunningOutEffect = true;
end

-- 冒险者冈布奥和勇者冈布奥同时飞离
function UIGuideDialogue:bothFlyOut(callback)
    -- 对话框先隐藏
    local bg = findChildByName(self.node, "CT/dialogue/bg" .. self.bgIndex);
    bg:setVisible(false);

    -- 冒险者冈布奥飞出
    local headIcon2 = findChildByName(self.node, "CT/dialogue/head_icon2");
    local oldPos = cc.p(headIcon2:getPosition());
    local move = cc.MoveTo:create(0.2, cc.p(oldPos.x + DESIGN_WIDTH, oldPos.y));
    local callFunc = cc.CallFunc:create(function()
        callback();
    end);
    headIcon2:runAction(cc.Sequence:create(move, callFunc));

    -- 勇者冈布奥飞离
    local headIcon1 = findChildByName(self.node, "CT/dialogue/head_icon1");
    local oldPos = cc.p(headIcon1:getPosition());
    local move = cc.MoveTo:create(0.2, cc.p(oldPos.x - DESIGN_WIDTH, oldPos.y));
    headIcon1:runAction(move);
end

-- 勇者冈布奥离开
function UIGuideDialogue:braveLeft(callback)
    local headIcon1 = findChildByName(self.node, "CT/dialogue/head_icon1");
    headIcon1:setScaleX(-headIcon1:getScaleX());
    local delay = cc.DelayTime:create(0.5);
    local move = cc.MoveBy:create(0.1, cc.p(-600, 0));
    local callFunc = cc.CallFunc:create(function()
        callback();
    end);
    headIcon1:runAction(cc.Sequence:create(delay, move, callFunc));
end
